#include "expr.h"
#include "string.h"

#define NUM 1
token tokens[70] = {0};

int nr_token = 0;

bool isoperand(int type) { return type == NUM; }
bool isoperator(int type) {
  return type == '+' || type == '-' || type == '*' || type == '/' ||
         type == '(' || type == ')';
}

bool isnum(int c) { return c <= '9' && c >= '0'; }
int priority_of_operator(int operator) {
  switch (operator) {
  case '*':
    return 4;
  case '/':
    return 4;
  case '+':
    return 5;
  case '-':
    return 5;
  default:
    return 14;
  }
  return 0;
}

bool priority_cmp(int operator1, int operator2) {
  return priority_of_operator(operator2) >= priority_of_operator(operator1);
}

bool check_parentheses(unsigned p, unsigned q) {
  return tokens[p].type == '(' && tokens[q].type == ')';
}
bool make_token(char *e) {
  int position = 0;
  int i;

  nr_token = 0;

  while (e[position] != '\0') {
    if (e[position] == ' ') {
      position++;
      continue;
    } else if (isnum(e[position])) {
      int i = atoi(e + position, 10);
      tokens[nr_token].type = NUM;
      tokens[nr_token].val = i;
      while (isnum(e[position])) {
        position++;
      }

      nr_token++;
    } else if (e[position] == '*') {
      position++;
      tokens[nr_token].type = '*';
      nr_token++;
    } else if (e[position] == '+') {
      position++;
      tokens[nr_token].type = '+';
      nr_token++;
    } else if (e[position] == '/') {
      position++;
      tokens[nr_token].type = '/';
      nr_token++;
    } else if (e[position] == '-') {
      position++;
      tokens[nr_token].type = '-';
      nr_token++;
    } else if (e[position] == '(') {
      position++;
      tokens[nr_token].type = '(';
      nr_token++;
    } else if (e[position] == ')') {
      position++;
      tokens[nr_token].type = ')';
      nr_token++;
    } else {
      return 0;
    }
  }
  return 1;
}

bool eval_func_success = 1;

int eval_expression(uint32_t p, uint32_t q) {
  if (!eval_func_success) {
    return 1;
  }
  if (p > q) {

    eval_func_success = 0;
    return 0;
  } else if (p == q) {
    uint32_t val = 0;
    bool success = 0;
    if (tokens[p].type == NUM) {
      return tokens[p].val;
    } else {
      eval_func_success = 0;
      return 1;
    }

  } else if (check_parentheses(p, q)) {
    int i = 0;
    int count_of_parentheses = 0;
    for (i = p; i <= q && count_of_parentheses >= 0; i++) {

      if (tokens[i].type == '(')
        count_of_parentheses += 1;
      else if (tokens[i].type == ')')
        count_of_parentheses -= 1;
    }
    if (count_of_parentheses == 0)
      return eval_expression(p + 1, q - 1);
    eval_func_success = 0;
    return 1;
  } else {
    int op = -1;
    int count_of_parentheses = 0;
    for (int i = p; i <= q; i++) {
      if (isoperator(tokens[i].type)) {
        if (tokens[i].type == '(') {
          count_of_parentheses += 1;
        } else if (tokens[i].type == ')') {
          count_of_parentheses -= 1;
        } else if (op == -1 && count_of_parentheses == 0) {
          op = i;
        } else if (count_of_parentheses == 0 &&
                   priority_cmp(tokens[op].type, tokens[i].type)) {
          op = i;
        }
      }
      //	else{
      //		printf("type = %d\n", tokens[i].type);
      //		fflush(stdout);
      //		}
      if (count_of_parentheses < 0) {
        eval_func_success = 0;
        return 1;
      }
    }
    int val1 = 0, val2 = 0;
    switch (tokens[op].type) {
    case '+':
      val1 = eval_expression(p, op - 1);
      val2 = eval_expression(op + 1, q);
      return val1 + val2;
    case '-':
      val1 = eval_expression(p, op - 1);
      val2 = eval_expression(op + 1, q);
      return val1 - val2;
    case '*':
      val1 = eval_expression(p, op - 1);
      val2 = eval_expression(op + 1, q);
      return mul(val1, val2);
    case '/':
      val1 = eval_expression(p, op - 1);
      val2 = eval_expression(op + 1, q);
      return div(val1, val2);
    default:
      eval_func_success = 0;
      return 1;
    }
  }
  return 0;
}

char *eval(char *s, char *buf) {
  //  return s;
  if (!make_token(s)) {
    strcpy(buf, unknown);
    return buf;
  }
  eval_func_success = 1;
  int val = eval_expression(0, nr_token - 1);
  if (eval_func_success) {
    if (val == 0) {
      buf = "0";
      buf[1] = 0;
      return buf;
    } else
      return itoa(val, buf, 10);
  }
  strcpy(buf, unknown);
  return buf;
}
